function(generate_castxml_commandline_flags)
  # this function sets up all the command line arguments needed
  # to successfully run castxml to generate the xml.

  # Before this function is run, the variables @CASTXML_INCLUDES@, @WRAPPER_MODULE_NAME@, @CASTXML_TYPEDEFS@, @CASTXML_FORCE_INSTANTIATE@
  # need to have been set so that the they can be expanded into the cxx file that is used
  # for generating the xml AST from the cxx_file.

  ## = Setup standard compiler flags===========================
  if(NOT ITK_USE_SYSTEM_CASTXML)
    # ExternalProject target for CastXML.
    set(_castxml_depends castxml)
  endif()

  unset(_ccache_cmd)
  if(ITK_USE_CCACHE)
    set(_ccache_cmd ${CCACHE_EXECUTABLE})
  endif()

  # Avoid missing omp.h include
  set(_castxml_cc_flags ${CMAKE_CXX_FLAGS})
  if(CMAKE_CXX_EXTENSIONS)
    set(
      _castxml_cc_flags
      "${_castxml_cc_flags} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_EXTENSION_COMPILE_OPTION}"
    )
  else()
    set(
      _castxml_cc_flags
      "${_castxml_cc_flags} ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION}"
    )
  endif()

  # Aggressive optimization flags cause cast_xml to give invalid error conditions
  set(
    INVALID_OPTIMIZATION_FLAGS
    "-fopenmp;-march=[a-zA-Z0-9\-]*;-mtune=[a-zA-Z0-9\-]*;-mfma"
  )
  foreach(rmmatch ${INVALID_OPTIMIZATION_FLAGS})
    string(REGEX REPLACE ${rmmatch} "" _castxml_cc_flags "${_castxml_cc_flags}")
  endforeach()
  unset(INVALID_OPTIMIZATION_FLAGS)

  # Configure the internal Clang preprocessor and target platform to match that of the given compiler command.
  separate_arguments(_castxml_cc_flags)
  unset(_castxml_cc)
  if(MSVC)
    set(
      _castxml_cc
      --castxml-cc-msvc
      (
        "${CMAKE_CXX_COMPILER}"
        ${_castxml_cc_flags}
      )
      -fexceptions
    )
    if(MSVC90)
      # needed for VS2008 64 bit
      set(
        _castxml_cc
        ${_castxml_cc}
        "-D_HAS_TR1=0"
      )
    endif()
  else()
    set(
      _castxml_cc
      --castxml-cc-gnu
      (
        "${CMAKE_CXX_COMPILER}"
        ${_castxml_cc_flags}
      )
    )
  endif()

  # Override castxml target platform when cross compiling
  unset(_target)
  if(CMAKE_CROSSCOMPILING)
    if(NOT CMAKE_CXX_COMPILER_TARGET)
      message(
        FATAL_ERROR
        "Set the target triple in CMAKE_CXX_COMPILER_TARGET "
        " as described in https://clang.llvm.org/docs/CrossCompilation.html"
      )
    endif()
    set(_target "--target=${CMAKE_CXX_COMPILER_TARGET}")
  endif()

  unset(_build_env)
  if(APPLE)
    # If building on OS X, make sure that CastXML's calls to the compiler have the
    # settings that the output files will be compiled with.  This prevents headers
    # from one version of OS X from being used when building for another version.
    list(
      APPEND
      _build_env
      env
      "SDKROOT=${CMAKE_OSX_SYSROOT}"
      "MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}"
    )
  endif()
  ## ============================

  # create the files used to pass the file to include to castxml
  get_directory_property(include_dir_list INCLUDE_DIRECTORIES)
  list(REMOVE_DUPLICATES include_dir_list)

  # CONFIG_CASTXML_INC_CONTENTS - variable used for building contents to write with configure_file()
  unset(CONFIG_CASTXML_INC_CONTENTS)
  foreach(dir ${include_dir_list})
    set(
      CONFIG_CASTXML_INC_CONTENTS
      "${CONFIG_CASTXML_INC_CONTENTS}\"-I${dir}\"\n"
    )
  endforeach()
  unset(include_dir_list)

  set(
    CONFIG_CASTXML_INC_CONTENTS
    "${CONFIG_CASTXML_INC_CONTENTS}-Qunused-arguments\n"
  )
  set(
    CONFIG_CASTXML_INC_CONTENTS
    "${CONFIG_CASTXML_INC_CONTENTS}-DITK_WRAPPING_PARSER\n"
  )
  set(
    CONFIG_CASTXML_INC_CONTENTS
    "${CONFIG_CASTXML_INC_CONTENTS}-DITK_MANUAL_INSTANTIATION\n"
  )

  # Get the compile_definitions of the module added with add_compile_definitions
  # From the wrapping folder (current)
  get_directory_property(compile_definition_list COMPILE_DEFINITIONS)
  # And from the top module folder
  set(module_folder "${WRAPPER_LIBRARY_SOURCE_DIR}/..")
  get_directory_property(
    compile_definition_list_at_module
    DIRECTORY "${module_folder}"
    COMPILE_DEFINITIONS
  )
  unset(module_folder)
  # Merge and remove duplicates
  list(APPEND compile_definition_list ${compile_definition_list_at_module})
  unset(compile_definition_list_at_module)
  list(REMOVE_DUPLICATES compile_definition_list)

  foreach(def ${compile_definition_list})
    set(
      CONFIG_CASTXML_INC_CONTENTS
      "${CONFIG_CASTXML_INC_CONTENTS}\"-D${def}\"\n"
    )
  endforeach()
  unset(compile_definition_list)
  foreach(include_file ${WRAPPER_INCLUDE_FILES})
    if("${include_file}" MATCHES "<.*>")
      string(APPEND CASTXML_INCLUDES "#include ${include_file}\n")
    else()
      string(APPEND CASTXML_INCLUDES "#include \"${include_file}\"\n")
    endif()
  endforeach()

  #Write compile definitions and include paths to file.  @CONFIG_CASTXML_INC_CONTENTS@ expanded in configure_file
  set(
    castxml_inc_file
    "${WRAPPER_LIBRARY_OUTPUT_DIR}/castxml_inputs/${WRAPPER_LIBRARY_NAME}.castxml.inc"
  )
  configure_file(
    "${ITK_WRAP_CASTXML_SOURCE_DIR}/cast_xml.inc.in"
    "${castxml_inc_file}"
    @ONLY
  )
  unset(CONFIG_CASTXML_INC_CONTENTS)

  # write the wrap_*.cxx file
  # Create the cxx file which will be given to castxml.
  set(
    cxx_file
    "${WRAPPER_LIBRARY_OUTPUT_DIR}/castxml_inputs/${_each_submodule_this_module}.cxx"
  )

  # The wrap_.cxx.in file expands the following variables:
  # @CASTXML_INCLUDES@, @WRAPPER_MODULE_NAME@, @CASTXML_TYPEDEFS@, @CASTXML_FORCE_INSTANTIATE@
  configure_file(
    "${ITK_WRAP_CASTXML_SOURCE_DIR}/wrap_.cxx.in"
    "${cxx_file}"
    @ONLY
  )
  unset(CASTXML_INCLUDES)

  # ====== Get list of include files that may trigger needing a re-write of castxml files
  set(_include ${${WRAPPER_LIBRARY_NAME}_SOURCE_DIR}/include)
  if(EXISTS ${_include})
    file(GLOB_RECURSE glob_hdrs ${_include}/*.h)
  endif()
  foreach(header IN LISTS glob_hdrs)
    get_filename_component(header_name ${header} NAME)
    if(${header_name} IN_LIST WRAPPER_INCLUDE_FILES)
      list(APPEND _hdrs ${header})
    endif()
  endforeach()

  # ===== Run the castxml command
  add_custom_command(
    OUTPUT
      ${xml_file}
    COMMAND
      ${_build_env} ${_ccache_cmd} ${CASTXML_EXECUTABLE} -o ${xml_file}
      --castxml-gccxml ${_target} --castxml-start _wrapping_ ${_castxml_cc} -w
      -c # needed for ccache to think we are not calling for link
      @${castxml_inc_file} ${cxx_file}
    VERBATIM
    DEPENDS
      ${_castxml_depends}
      ${cxx_file}
      ${castxml_inc_file}
      ${_hdrs}
  )
  unset(cxx_file)
  unset(castxml_inc_file)
  unset(_build_env)
  unset(_target)
  unset(_castxml_cc)
  unset(_castxml_cc_flags)
  unset(_ccache_cmd)
  unset(_castxml_depends)
endfunction()

function(test_lib_module_names_different)
  # We run into some trouble if there's a module with the same name as the
  # wrapper library. Fix this.
  string(TOUPPER "${_each_submodule_this_module}" upper_module)
  string(TOUPPER "${WRAPPER_LIBRARY_NAME}" upper_lib)
  if("${upper_module}" STREQUAL "${upper_lib}")
    message(
      FATAL_ERROR
      "The module ${_each_submodule_this_module} can't have the same name as its library. Note that the names are not case sensitive."
    )
  endif()
  unset(upper_lib)
  unset(upper_module)
  ## RETURN VALUES TO PARENT SCOPE
  set(WRAPPER_INCLUDE_FILES "${WRAPPER_INCLUDE_FILES}" PARENT_SCOPE)
endfunction()

function(generate_swig_interface_in_file)
  # Uses global variables WRAPPER_INCLUDE_FILES, SWIG_INTERFACE_TYPEDEFS
  # store the content of the SwigInterface.h files - a set of #includes for that module
  foreach(include_file ${WRAPPER_INCLUDE_FILES})
    list(APPEND _SWIG_INTERFACE_INCLUDES ${include_file})
  endforeach()

  if(_SWIG_INTERFACE_INCLUDES)
    list(REMOVE_DUPLICATES _SWIG_INTERFACE_INCLUDES)
    foreach(include_file ${_SWIG_INTERFACE_INCLUDES})
      if("${include_file}" MATCHES "<.*>")
        string(
          APPEND
          SWIG_INTERFACE_INCLUDES_CONTENT
          "#include ${include_file}\n"
        )
      else()
        string(
          APPEND
          SWIG_INTERFACE_INCLUDES_CONTENT
          "#include \"${include_file}\"\n"
        )
      endif()
    endforeach()
  endif()

  # create the file which stores all of the includes
  set(
    _includes_file
    "${WRAPPER_LIBRARY_OUTPUT_DIR}/castxml_inputs/${_each_submodule_this_module}SwigInterface.h.in"
  )
  # module.includes.in expands cmake variables: @SWIG_INTERFACE_INCLUDES_CONTENT@  #@SWIG_INTERFACE_TYPEDEFS@
  configure_file(
    "${ITK_WRAP_SWIGINTERFACE_SOURCE_DIR}/module.includes.in"
    ${_includes_file}
    @ONLY
  )
endfunction()

function(generate_wrap_doc)
  set(
    _doxy2swig_config_file
    ${CMAKE_CURRENT_BINARY_DIR}/Doc/${_each_submodule_this_module}.conf
  )
  configure_file(
    "${ITK_WRAP_DOC_SOURCE_DIR}/itk_doxy2swig.conf.in"
    "${_doxy2swig_config_file}"
    @ONLY
  )
  # run itk_doxy2swig
  set(_itk_doxy2swig_py "${ITK_WRAP_DOC_SOURCE_DIR}/itk_doxy2swig.py")
  add_custom_command(
    OUTPUT
      ${swig_doc_interface_file}
    COMMAND
      ${Python3_EXECUTABLE} ${_itk_doxy2swig_py} ${_doxy2swig_config_file}
      ${swig_doc_interface_file}
    #DEPENDS ${ITK_WRAP_DOC_DOXYGEN_XML_FILES} ${_doxy2swig_config_file} ${_itk_doxy2swig_py}
    DEPENDS
      ${WRAPPER_LIBRARY_NAME}Doxygen
      ${_doxy2swig_config_file}
      ${_itk_doxy2swig_py}
    #    COMMENT "-- Wrapping library ${_each_submodule_this_module}: Generating swig interface for inline documentation."
  )
endfunction()

macro(itk_auto_load_submodules)
  # Global vars used: WRAPPER_LIBRARY_NAME WRAPPER_DEFAULT_INCLUDE
  #                   WRAPPER_LIBRARY_SOURCE_DIR WRAPPER_LIBRARY_OUTPUT_DIR
  #                   WRAPPER_SUBMODULE_ORDER -- order specified for this submodule, unset after use in this module
  # Global vars modified: WRAPPER_TYPEDEFS
  #                       WRAPPER_INCLUDE_FILES --
  #                       WRAPPER_AUTO_INCLUDE_HEADERS
  #                       SWIG_INTERFACE_MDX_CONTENT --

  # Include the *.wrap files in WRAPPER_LIBRARY_SOURCE_DIR. This causes
  # corresponding wrap_*.cxx files to be generated WRAPPER_LIBRARY_OUTPUT_DIR,
  # and added to the WRAPPER_LIBRARY_SWIG_INPUTS list.
  # In addition, this causes the other required wrap_*.cxx files for the entire
  # library and each wrapper language to be created.
  # This macro causes the language support files for the templates and
  # library here defined to be created.

  # Now search for other *.wrap files to include
  file(GLOB _wrap_cmake_files "${WRAPPER_LIBRARY_SOURCE_DIR}/*.wrap")
  # sort the list of files so we are sure to always get the same order on all system
  # and for all builds. That's important for several reasons:
  # - the order is important for the order of creation of python template
  # - the typemaps files are always the same, and the rebuild can be avoided
  list(SORT _wrap_cmake_files)
  set(THIS_MODULE_SUBMODULE_ORDER ${WRAPPER_SUBMODULE_ORDER})
  unset(WRAPPER_SUBMODULE_ORDER)

  foreach(_file ${_wrap_cmake_files})
    # get the module name from module.wrap
    get_filename_component(_module_from_file "${_file}" NAME_WE)
    # append found implied modules to end of proscribed ordering
    list(APPEND THIS_MODULE_SUBMODULE_ORDER "${_module_from_file}")
  endforeach()
  unset(_wrap_cmake_files)
  unset(_file)
  list(REMOVE_DUPLICATES THIS_MODULE_SUBMODULE_ORDER)
  foreach(_each_submodule_this_module ${THIS_MODULE_SUBMODULE_ORDER})
    # include a cmake module file and generate the associated wrap_*.cxx file.
    # This basically sets the global vars that will be added to or modified
    # by the commands in the included *.wrap module.
    message(
      STATUS
      "${WRAPPER_LIBRARY_NAME}: Creating ${_each_submodule_this_module} submodule."
    )

    test_lib_module_names_different()

    # call generator specific logic to set several associated global variables
    # clear the typedefs and the includes
    unset(CASTXML_TYPEDEFS)
    unset(CASTXML_FORCE_INSTANTIATE)

    # typedefs for swig
    unset(SWIG_INTERFACE_TYPEDEFS)

    unset(ITK_WRAP_DOC_DOXY2SWIG_INPUT) # the c++ name - swig names definitions

    # WRAPPER_INCLUDE_FILES: contains a list of all files to include in the final cxx file
    unset(WRAPPER_INCLUDE_FILES)

    # Add WRAPPER_DEFAULT_INCLUDE to the list of files in WRAPPER_INCLUDE_FILES
    # to be #included in the final cxx file
    foreach(inc ${WRAPPER_DEFAULT_INCLUDE})
      # MODIFIES WRAPPER_INCLUDE_FILES
      itk_wrap_include("${inc}")
    endforeach()

    #MODIFIES ITK_WRAP_PYTHON_SWIG_EXT ITK_WRAP_PYTHON_LIBRARY_IMPORTS
    itk_wrap_submodule_python("${_each_submodule_this_module}" "${WRAPPER_LIBRARY_NAME}")

    dump_cmake_variables("prelogger_${_each_submodule_this_module}" "${_each_submodule_this_module}")
    # Indicates that the appropriate itk header for this class will be automatically included
    # in later stages of the wrapping process
    set(WRAPPER_AUTO_INCLUDE_HEADERS ON)

    # Now include the .wrap file, and read manually requested types needed
    # to build lists of items to be wrapped for both CASTXML and SWIG
    if(
      EXISTS
        "${WRAPPER_LIBRARY_SOURCE_DIR}/${_each_submodule_this_module}.wrap"
    )
      include(
        "${WRAPPER_LIBRARY_SOURCE_DIR}/${_each_submodule_this_module}.wrap"
      )
    else()
      # for backward compatibility, to be removed in ITKv6
      if(
        EXISTS
          "${WRAPPER_LIBRARY_SOURCE_DIR}/wrap_${_each_submodule_this_module}.cmake"
      )
        message(
          FATAL_ERROR
          "INCORRECT FILE NAME PATTERN: ${WRAPPER_LIBRARY_SOURCE_DIR}/wrap_${_each_submodule_this_module}.cmake should be named ${WRAPPER_LIBRARY_SOURCE_DIR}/${_each_submodule_this_module}.cmake"
        )
      endif()
      message(
        SEND_ERROR
        "Module ${WRAPPER_LIBRARY_SOURCE_DIR}/${_each_submodule_this_module}.wrap not found."
      )
    endif()

    dump_cmake_variables("postlogger_${_each_submodule_this_module}" "${_each_submodule_this_module}")
    write_changed_cmake_variables_to_file(
          "${WRAPPER_LIBRARY_OUTPUT_DIR}/diff_${_each_submodule_this_module}.diff"
          "${prelogger_${_each_submodule_this_module}}"
          "${postlogger_${_each_submodule_this_module}}"
          "${_each_submodule_this_module}"
    )

    # ======== RUN COMMANDS FOR GENERATING XML AST information
    # write the module.xml file using castxml the xml file to be generated
    set(
      xml_file
      "${WRAPPER_LIBRARY_OUTPUT_DIR}/castxml_inputs/${_each_submodule_this_module}.xml"
    )
    ## @CASTXML_INCLUDES@, @WRAPPER_MODULE_NAME@, @CASTXML_TYPEDEFS@, @CASTXML_FORCE_INSTANTIATE@
    generate_castxml_commandline_flags()
    list(APPEND CastXML_OUTPUT_FILES ${xml_file})
    unset(xml_file)

    # store the path of the idx file to store it in the mdx file
    string(
      APPEND
      SWIG_INTERFACE_MDX_CONTENT
      "${_each_submodule_this_module}.idx\n"
    )
    string(
      APPEND
      SWIG_INTERFACE_MODULE_CONTENT
      "%import ${_each_submodule_this_module}.i\n"
    )
    generate_swig_interface_in_file()

    itk_end_wrap_submodule_python("${_each_submodule_this_module}")
    if(${module_prefix}_WRAP_DOC)
      set(
        swig_doc_interface_file
        ${WRAPPER_MASTER_INDEX_OUTPUT_DIR}/${_each_submodule_this_module}_doc.i
      )
      generate_wrap_doc()
      list(APPEND ITK_WRAP_DOC_DOCSTRING_FILES ${swig_doc_interface_file})
      unset(swig_doc_interface_file)
    endif()
  endforeach()
  unset(_each_submodule_this_module)
endmacro()
